package org.abstractmeta.commandify.core.command.reflectify.list;


import org.abstractmeta.commandify.core.command.AbstractReflectify;
import org.abstractmeta.reflectify.Accessor;
import org.abstractmeta.reflectify.MethodInvoker;
import org.abstractmeta.reflectify.Mutator;
import org.abstractmeta.reflectify.ParameterSetter;

import java.lang.reflect.Type;
import java.util.*;

/**
 * Represents ListReflectify
 * <p>
 * </p>
 *
 * @author Adrian Witas
 */
public class ListReflectify<T extends List> extends AbstractReflectify<T> {

    @SuppressWarnings("unchecked")
    public ListReflectify() {
        super(Class.class.cast(Collection.class));
    }


    @Override
    protected Map<String, Accessor> getAccessors() {
        return Collections.emptyMap();
    }

    @Override
    protected Map<String, Mutator> getMutators() {
        return Collections.emptyMap();
    }


    @Override
    public Provider<T> getProvider(Class... argumentTypes) {
        return new Provider<T>() {

            @Override
            public <T> ParameterSetter<T> getParameterSetter(Class<T> argumentType, int argumentIndex) {
                throw new UnsupportedOperationException();
            }

            @Override
            public ParameterSetter<Object> getParameterSetter(int argumentIndex) {
                throw new UnsupportedOperationException();
            }

            @Override
            public Type[] getGenericParameterTypes() {
                return new Type[0];
            }

            @Override
            public Class[] getParameterTypes() {
                return new Class[0];
            }

            @Override
            @SuppressWarnings("unchecked")
            public T get() {
                return (T) new ArrayList();
            }
        };
    }


    @Override
    @SuppressWarnings("unchecked")
    public <T1> MethodInvoker<T, T1> getMethodInvoker(Class<T1> methodResultType, String methodName, Class... argumentTypes) {
        if ("add".equals(methodName)) {
            return new AddMethodInvoker();

        }
        if ("get".equals(methodName)) {
            return new GetMethodInvoker();
        }
        throw new IndexOutOfBoundsException("Invalid index parameter");
    }

    public static class AddMethodInvoker<T> implements MethodInvoker<List, T> {

        private Object item;

        @Override
        public <T1> ParameterSetter<T1> getParameterSetter(Class<T1> argumentType, int argumentIndex) {
            final AddMethodInvoker self = this;
            if (argumentIndex == 0) {
                return new ParameterSetter<T1>() {

                    @Override
                    public void set(T1 value) {
                        self.item = value;

                    }
                };
            }
            throw new IndexOutOfBoundsException("Invalid index parameter");
        }

        @Override
        public ParameterSetter<Object> getParameterSetter(int argumentIndex) {
            return getParameterSetter(Object.class, argumentIndex);
        }

        @Override
        @SuppressWarnings("unchecked")
        public T invoke(List instance) {
            return (T) (Boolean) instance.add(item);
        }

        @Override
        public Type[] getGenericParameterTypes() {
            return new Type[0];
        }

        @Override
        public Class[] getParameterTypes() {
            return new Class[0];
        }

        @Override
        public Type getResultGenericType() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Class getResultType() {
            throw new UnsupportedOperationException();
        }
    }


    public static class GetMethodInvoker<T> implements MethodInvoker<Map, T> {

        private Object index;


        @Override
        public <T1> ParameterSetter<T1> getParameterSetter(Class<T1> argumentType, int argumentIndex) {
            final GetMethodInvoker self = this;
            if (argumentIndex == 0) {
                return new ParameterSetter<T1>() {

                    @Override
                    public void set(T1 value) {
                        self.index = value;

                    }
                };
            }
            throw new IndexOutOfBoundsException("Invalid index parameter");
        }

        @Override
        public ParameterSetter<Object> getParameterSetter(int argumentIndex) {
            return getParameterSetter(Object.class, argumentIndex);
        }

        @Override
        @SuppressWarnings("unchecked")
        public T invoke(Map instance) {
            return (T) instance.get(index);
        }

        @Override
        public Type[] getGenericParameterTypes() {
            return new Type[0];
        }

        @Override
        public Class[] getParameterTypes() {
            return new Class[0];
        }

        @Override
        public Type getResultGenericType() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Class getResultType() {
            throw new UnsupportedOperationException();
        }
    }


}
